package com.yulinlin.jdbc.session;


import com.yulinlin.core.ISqlTemplate;
import com.yulinlin.lang.util.ReflectionUtil;
import com.yulinlin.lang.util.StringUtil;
import com.yulinlin.jdbc.model.anno.JoinField;
import com.yulinlin.jdbc.serialize.impl.ITypeSerialize;
import com.yulinlin.core.wrapper.ISqlWrapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.PreparedStatementSetter;
import org.springframework.jdbc.core.RowMapper;

import java.lang.reflect.Field;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


public class SqlTemplate implements ISqlTemplate {

    private static final Logger log = LoggerFactory.getLogger(SqlTemplate.class);

    private JdbcTemplate jdbcTemplate;


    private Map<Class,ITypeSerialize>  iTypeSerializeMap;



    public SqlTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
        this.iTypeSerializeMap = new HashMap<>();
    }

    //注册一个类型拦截器
    public void registerTypeSerialize(ITypeSerialize serialize){
        iTypeSerializeMap.put(serialize.getTypeClass(),serialize);
    }

    public void registerTypeSerialize(List<ITypeSerialize> serializes){
        for (ITypeSerialize s: serializes){
            registerTypeSerialize(s);
        }
    }

    private ITypeSerialize getTypeSerialize(Class clazz){
        ITypeSerialize iTypeSerialize =   iTypeSerializeMap.get(clazz);

        if(iTypeSerialize == null){
            for (ITypeSerialize serialize : iTypeSerializeMap.values()){
                if(serialize.supportExtend()){
                    if(serialize.getTypeClass().isAssignableFrom(clazz)){
                        return  serialize;
                    }
                }
            }

            throw new RuntimeException("缺少类型转换器:"+clazz.getName());
        }
        return  iTypeSerialize;

    }



    private PreparedStatementSetter getPreparedStatementSetter(List list) {
        PreparedStatementSetter pss = new ArrayPreparedStatementSetter(list);
        return pss;
    }


    public int execute(ISqlWrapper jw) {
        SqlNode node = new SqlNode(jw);
        PreparedStatementSetter pre = getPreparedStatementSetter(node.getList());
        log.info(node.getSql());

        return jdbcTemplate.update(node.getSql(), pre);
    }


    public <E> List<E> select(ISqlWrapper jw, Class<E> clazz) {
        SqlNode node = new SqlNode(jw);
        PreparedStatementSetter pre = getPreparedStatementSetter(node.getList());

        RowMapper rowMapper = new ClassRowMapper(clazz);
        log.info(node.getSql());
        return jdbcTemplate.query(node.getSql(), pre, rowMapper);


    }



    private class ClassRowMapper implements RowMapper {

        private Class clazz;

        public ClassRowMapper(Class clazz) {
            this.clazz = clazz;
        }

        @Override
        public Object mapRow(ResultSet rs, int rowNum) throws SQLException {

            ResultSetMetaData metaData =  rs.getMetaData();
            Map map =  new HashMap<>();
            int columnTotal =  metaData.getColumnCount();
            for(int i = 1;i<=columnTotal;i++){
                String columnName =  metaData.getColumnLabel(i);
                String key = StringUtil.columnToJava(columnName);
                Object val =  rs.getObject(i);
                map.put(key,val);
            }

            if(clazz == null){
                return  map;
            }


            Object entity = ReflectionUtil.newInstance(clazz);
            for (Field field : ReflectionUtil.getAllDeclaredFields(clazz)) {
                String columnName = field.getName();
                JoinField joinField =  AnnotationUtils.findAnnotation(field,JoinField.class);

                if(joinField== null || joinField.exist() == false){
                    continue;
                }

                Object val =  map.get(columnName);
                if(val == null){
                    continue;
                }
                Class fieldType =  field.getType();

                //如果类型不严格相等，则转换
                if(val.getClass() != fieldType){
                    //根据字段字段类型获取转换器
                    ITypeSerialize iFieldSerialize  =   getTypeSerialize(fieldType);
                    //转换
                    val = iFieldSerialize.unSerialize(val,field);
                }
                ReflectionUtil.invokeSetter(entity, columnName, val);

            }
            return entity;
        }



    }


    private class ArrayPreparedStatementSetter implements PreparedStatementSetter {

        private List list;

        public ArrayPreparedStatementSetter(List list) {
            this.list = list;
        }

        @Override
        public void setValues(PreparedStatement ps) throws SQLException {
            for (int i = 0; i < list.size(); i++) {
                Object value = list.get(i);
                int ind = i + 1;
                if(value instanceof Boolean) {
                    ps.setBoolean(ind, (Boolean) value);
                }else if(value instanceof Byte){
                   ps.setByte(ind,(byte)value);
                }else if(value instanceof Short){
                    ps.setShort(ind,(short)value);
                } else if (value instanceof Integer) {
                    ps.setInt(ind, (int) value);
                } else if (value instanceof Long) {
                    ps.setLong(ind, (long) value);
                }
                else if (value instanceof Float) {
                    ps.setFloat(ind, (float) value);
                }
                else if (value instanceof Double) {
                    ps.setDouble(ind, (Double) value);
                }
                else if (value instanceof String) {
                    ps.setString(ind, (String) value);
                }else{
                    //转换器转换基本类型
                    ITypeSerialize iFieldSerialize = getTypeSerialize(value.getClass());
                    value  =  iFieldSerialize.serialize(value);
                    ps.setObject(ind,value);
                }

            }
        }
    }
}